//所以我们一行行读出来，然后使用逗号分隔开
//将块大小设定为4K
#define PAGE_SIZE 1024

//每一行的最大字节数，同时也是行缓冲区的大小
#define MAX_LINE_SIZE 1024

//预测深度的最大值
#define MAX_PREDICT_DEEP 20

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pageread-predictor.h"
#include "readcache-with-LRU.h"
#include "Noah-predictor.h"
#include "mode-judge.h"
#include "markov-predictor.h"

//关于这个函数我之前的处理有问题，实际上我顾头不顾尾，实际上我们不仅仅要考虑起始位置
//还需要考虑结束的位置，所以offset也要考虑

int main(void){
    FILE *fp_read = NULL;
    FILE *fp_write = NULL;
    char line[MAX_LINE_SIZE];
    
    //循环变量
    long j;
    int i;
    

    long* predict_seq;
    long* predict_seq2;
    long* predict_seq3;
    int size;
    int size2;
    int size3;

    //这里加入一个读缓存
    all_read_cache_t read_caches;
    //初始化读缓存
    init_readcache_meta(&read_caches);

    //初始化一个函数来看看缓存命中率
    long all_count_of_block = 0;
    long hit_count_of_block = 0;
    long noah_predict_time = 0;
    long stable_predictor_time = 0;

    //初始化一个预测序列
    predict_seq = (long*)malloc(MAX_PREDICT_DEEP*sizeof(long));
    //初始化给noah用的第二个序列
    predict_seq2 = (long*)malloc(MAX_PREDICT_DEEP*sizeof(long));
    predict_seq3 = (long*)malloc(MAX_PREDICT_DEEP*sizeof(long));


    //初始化noah
    Noah_predictor_t noah;
    Noah_predictor_t stable_noah;
    Noah_predictor_init(&noah, 1);
    Noah_predictor_init(&stable_noah, 12);

    //初始化mode_judger
    mode_judge_t mode_judger;
    init_mode_judge(&mode_judger);
    

    printf("开始运行\n");

    if((fp_read = fopen("resource/MSR-Cambridge/hm_0.csv","r")) != NULL)
    {   
        printf("开始读取\n");

        char delims[] = ",";
        char *result = NULL;

        

        while (fgets(line, MAX_LINE_SIZE, fp_read)){
            //首先看看是不是read，如果里面有read再打印
            //strstr是用来匹配子串的函数
            if(strstr(line, "Write") != NULL){
                continue;
            }

            //这里是个分割的元素
            result = strtok( line, delims );

            // printf("%s", result);

            //这里要进行计数，只保留第5列
            i = 1;

            //这里要看看起始地址和分页除的偏移，用来方便算被访问的块号
            // int first_offset = 0;
            
            //开始的页号
            long block_count;

            //结束的页号
            long end_block_count;

            //访问的首地址
            long start_location;

            //只要还有可以分的，那result不会是NULL
            while((result = strtok( NULL, delims))){
                i++;
                
                if(i == 5){
                    all_count_of_block++;
                    start_location = atol(result);

                    //将字符串换成数字
                    block_count = start_location / PAGE_SIZE;

                    // first_offset = atol(result) % PAGE_SIZE;

                    //这里将块访问记录导入到一个新的csv文件中
                    //这里更新元数据
                    update_when_access_page_cache(&mode_judger, block_count);
                    
                    int barrel_num = block_count % MAX_READ_CACHE_NUM;
                    if(access_read_cache(&(read_caches.read_cache_arr[barrel_num]), block_count) != NULL){
                        hit_count_of_block++;
                    }

                    add_cache_meta(&read_caches, block_count);
                    access_read_cache(&(read_caches.read_cache_arr[barrel_num]), block_count);
                    
                    //在外部将传入的预测序列初始化
                    memset(predict_seq, 0, MAX_PREDICT_DEEP*sizeof(long));
                    memset(predict_seq2, 0, MAX_PREDICT_DEEP*sizeof(long));
                    memset(predict_seq3, 0, MAX_PREDICT_DEEP*sizeof(long));

                    page_predictor(block_count, predict_seq, &size);
                    // noah_predictor(&noah, block_count, predict_seq2, &size2);
                    // noah_predictor(&stable_noah, block_count, predict_seq3, &size3);

                    printf("%ld\n", block_count);
                }

                //这里将offset也考虑在内
                if(i == 6){
                    all_count_of_block++;

                    //看看结束的数字
                    end_block_count = (atol(result) + start_location) / PAGE_SIZE;

                    //将开始和结束块之间的块放到缓存中
                    for(j = block_count + 1; j <= end_block_count; j++){
                        all_count_of_block++;
                        
                        int barrel_num = j % MAX_READ_CACHE_NUM;
                        
                        if(access_read_cache(&(read_caches.read_cache_arr[barrel_num]), j) != NULL){
                            hit_count_of_block++;
                        }

                        //在外部将传入的预测序列初始化
                        memset(predict_seq, 0, MAX_PREDICT_DEEP*sizeof(long));
                        memset(predict_seq2, 0, MAX_PREDICT_DEEP*sizeof(long));
                        memset(predict_seq3, 0, MAX_PREDICT_DEEP*sizeof(long));

                        page_predictor(j, predict_seq, &size);
                        // noah_predictor(&noah, j, predict_seq2, &size2);
                        // noah_predictor(&stable_noah, j, predict_seq3, &size3);

                        add_cache_meta(&read_caches, j);
                        access_read_cache(&(read_caches.read_cache_arr[barrel_num]), j);
                        
                        if(j == end_block_count){
                            //这里进行预读
                            if(size2 > 0){
                                // printf("从noah中预测的内容access_count%ld:", access_block_count);
                                // for(int j = 0 ; j < size2 ; j++){
                                //     printf("%ld,", predict_seq2[j]);
                                // }
                                // printf("\n");
                                noah_predict_time++;
                            }

                            if(size3 > 0){
                                stable_predictor_time++;
                            }

                            //这里看看预测成功率
                            if(size2 == 0 && get_mode_of_predictor(&mode_judger) == 1){
                                //这里可以单纯使用page的预测方法
                                for(i = 0; i < 3; i++){
                                    add_cache_meta(&read_caches, predict_seq[i]);
                                    update_and_judge_mode(&mode_judger, predict_seq[i]);
                                }
                            }

                            //单纯记录连续记录的成功率
                            for(i = 0; i < size; i++) {
                                update_and_judge_mode(&mode_judger, predict_seq[i]);
                            }

                            //这里只看看page
                            for(i = 0; i < size; i++){
                                add_cache_meta(&read_caches, predict_seq[i]);
                            }

                            // for(i = 0;  i < size2 && i < size; i++){
                            //     //当两个预测器预测地一样的时候才会写入
                            //     if(predict_seq[i] == predict_seq2[i]){
                            //         add_cache_meta(&read_caches, predict_seq2[i]);
                            //         // if(i == 0){
                            //         //     update_and_judge_mode(&mode_judger, predict_seq[i]);
                            //         // }
                            //     }else if(i < size3 && predict_seq2[i] == predict_seq3[i]){
                            //         add_cache_meta(&read_caches, predict_seq3[i]);
                            //     }
                            // }
                        }
                        printf("%ld\n", j);
                    }
                }
            }         
        }
        //关闭
        printf("读取完毕\n");
        fclose(fp_read);
        fp_read = NULL;
        
        //计算缓存命中率
        
        int hit_rate = hit_count_of_block  * 100 / all_count_of_block;

        //预测错误率
        //这里整理一下预测错误率
        long err_cache;
        for(int i = 0 ; i < MAX_READ_CACHE_NUM; i++){
            err_cache = err_cache + read_caches.read_cache_arr[i].page_cache_have_not_access;

            //将已有的数据拿出做一个分析计数
            read_cache_t* each_read_cache = &(read_caches.read_cache_arr[i]);

            read_cache_meta_t* scan_item = each_read_cache->head->next;

            while(scan_item != each_read_cache->head){
                if(scan_item->have_accessed == 0){
                    err_cache++;
                }
                scan_item = scan_item->next;
            }
        }
        long all_del_cache;
        for(int i = 0 ; i < MAX_READ_CACHE_NUM; i++){
            all_del_cache = all_del_cache + read_caches.read_cache_arr[i].page_cache_del;

            read_cache_t* each_read_cache = &(read_caches.read_cache_arr[i]);

            read_cache_meta_t* scan_item = each_read_cache->head->next;

            while(scan_item != each_read_cache->head){
                
                all_del_cache++;
                
                scan_item = scan_item->next;
            }
        }

        printf("所有弹出的缓存%ld\n", all_del_cache);
        printf("错误的次数%ld\n", err_cache);
        printf("noah预测的触发次数%ld\n", noah_predict_time);
        printf("稳定预测的触发次数%ld\n", stable_predictor_time);
        printf("总访问次数%ld\n", all_count_of_block);
        printf("缓冲命中率:%d%%\n", hit_rate);

        int err_rate = err_cache * 100 / all_del_cache;
        
        int predict_rate = noah_predict_time * 100 / all_count_of_block;
        printf("预测触发率:%d%%\n", predict_rate);
        printf("预测错误率:%d%%\n", err_rate);
    }
}